from flask import Blueprint, request, jsonify, current_app
from werkzeug.datastructures import FileStorage
import os
import copy
import json
import sys
from concurrent.futures import ThreadPoolExecutor
from threading import Lock


# 将搜索目录改为目录
sys.path.append(
    os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)
)
from app.utils import shared_executor, getResponse, SERVICE_DIR, STAGING_DIR, extract_service_info, get_service_staging_dir, is_compile_ready
from src.scripts.util.AtomServiceUtil import AtomServiceUtil
from src.scripts.util.DDSUtil import DDSUtil
from src.scripts.util.GrpcMethodUtil import GrpcMethodUtil
from src.scripts.util.Util import Util
from src.scripts.entity.AtomService import AtomService

# 创建蓝图
develop_bp = Blueprint("develop", __name__)


# 确保暂存目录存在
os.makedirs(STAGING_DIR, exist_ok=True)


@develop_bp.route("/uploadservice", methods=["POST"])
def uploadService():
    """
    上传微服务的文件
    """
    # 检查是否有文件数据
    cpp_file: FileStorage = request.files["cppfile"]
    hpp_file: FileStorage = request.files["hppfile"]
    # 检查文件名是否为空
    if cpp_file.filename == "" or hpp_file.filename == "":
        return getResponse(400, "cpp file name or hpp file name is empty")
    # 获取文件名和文件内容
    cpp_file_name: str = os.path.splitext(cpp_file.filename)[0]
    hpp_file_name: str = os.path.splitext(hpp_file.filename)[0]
    # 检查文件名是否相同
    if cpp_file_name != hpp_file_name:
        return getResponse(400, "cpp file name and hpp file name are not the same")
    else:
        service_name: str = cpp_file_name
        cpp_content: str = cpp_file.read().decode("utf-8")
        hpp_content: str = hpp_file.read().decode("utf-8")

    # 检查数据是否为空
    if service_name == "" or cpp_file == "" or hpp_file == "":
        # 深拷贝response_dict
        return getResponse(400, "service_name or cpp_content or hpp_content is empty")

    # 检查是否存在该服务
    if os.path.exists(os.path.join(SERVICE_DIR, service_name)):
        return getResponse(400, "service already exists")

    # 检查是否存在该暂存服务，如果存在，就删除该文件所有内容
    if os.path.exists(os.path.join(STAGING_DIR, service_name)):
        # 删除该文件所有内容
        os.system(f"rm -rf {os.path.join(STAGING_DIR, service_name)}/*")

    # 创建服务目录
    os.makedirs(os.path.join(STAGING_DIR, service_name))
    # 保存hpp和cpp文件
    with open(
        os.path.join(STAGING_DIR, service_name, f"{service_name}.h"),
        "w",
    ) as file:
        file.write(hpp_content)
    with open(
        os.path.join(STAGING_DIR, service_name, f"{service_name}.cpp"),
        "w",
    ) as file:
        file.write(cpp_content)

    """
    调用util函数进行数据处理
    """
    try:
        # 解析h文件，生成service对象
        service: AtomService = AtomServiceUtil.parseHpp(os.path.join(STAGING_DIR, service_name, f"{service_name}.h"))
        # 将grpc的信息写入json
        GrpcMethodUtil.add_info_to_json(service)
        async_process_service(service)
        # 获取service的json文件
        with open(
            os.path.join(SERVICE_DIR, service_name, f"{service_name}.json"),
            "r",
        ) as file:
            service_json = json.load(file)

        # 异步启动处理任务
        shared_executor.submit(async_process_service, service)

        # 成功时返回200状态码
        return getResponse(200, "服务成功上传", service_json)
    except Exception as e:
        # 捕获所有异常并返回错误信息
        current_app.logger.error(f"服务生成失败: {str(e)}")
        return getResponse(500, "服务生成失败", str(e))


@develop_bp.route("/uploadFile", methods=["POST"])
def upload_service_file():
    """
    统一文件上传接口：自动区分.h/.c/.cpp，保存后检查是否满足编译条件
    前端需用"file"参数上传文件（如form-data中key为"file"，value为文件）
    """
    # 1. 检查是否上传了文件
    if "file" not in request.files:
        return getResponse(400, "未上传文件，请使用参数名'file'")
    file: FileStorage = request.files["file"]
    filename: str = file.filename.strip()

    # 2. 解析文件名，获取服务名和文件类型
    try:
        service_name, file_type = extract_service_info(filename)
    except ValueError as e:
        return getResponse(400, str(e))

    # 3. 保存文件到服务暂存目录
    service_dir = get_service_staging_dir(service_name)
    os.makedirs(service_dir, exist_ok=True)  # 确保目录存在
    # 保存路径（统一用服务名+后缀，避免文件名不一致）
    save_path = os.path.join(service_dir, f"{service_name}{file_type}")

    try:
        file.save(save_path)  # 覆盖已存在的文件
        current_app.logger.info(f"文件保存成功：{save_path}")
    except Exception as e:
        error_msg = f"文件保存失败：{str(e)}"
        current_app.logger.error(error_msg)
        return getResponse(500, error_msg)

    # 4. 检查是否满足编译条件，满足则触发编译
    if is_compile_ready(service_name):
        current_app.logger.info(f"服务[{service_name}]文件齐全，触发编译")
        # 开始编译
        try:
            # 解析h文件，生成service对象
            service: AtomService = AtomServiceUtil.parseHpp(os.path.join(STAGING_DIR, service_name, f"{service_name}.h"))
            # 将grpc的信息写入json
            GrpcMethodUtil.add_info_to_json(service)
            async_process_service(service)
            # 获取service的json文件
            with open(
                os.path.join(SERVICE_DIR, service_name, f"{service_name}.json"),
                "r",
            ) as file:
                service_json = json.load(file)

            # 异步启动处理任务
            shared_executor.submit(async_process_service, service)

            # 成功时返回200状态码
            return getResponse(200, "服务成功上传", service_json)
        except Exception as e:
            # 捕获所有异常并返回错误信息
            current_app.logger.error(f"服务生成失败: {str(e)}")
            return getResponse(500, "服务生成失败", str(e))
    else:
        # 未满足编译条件，提示等待其他文件
        missing = []
        if not os.path.exists(os.path.join(STAGING_DIR, service_name, f"{service_name}.h")):
            missing.append(".h头文件")
        if not (
            os.path.exists(os.path.join(STAGING_DIR, service_name, f"{service_name}.c"))
            or os.path.exists(os.path.join(STAGING_DIR, service_name, f"{service_name}.cpp"))
        ):
            missing.append(".c或.cpp源文件")
        return getResponse(200, f"文件保存成功（{file_type}），等待补充：{', '.join(missing)}")
    
@develop_bp.route("/generateCppAndHppTemplate", methods=["POST"])
def generate_cpp_and_hpp_template():
    """
    生成cpp和hpp模板
    """
    # 检查是否上传了json文件
    json_file: FileStorage = request.files["json_file"]
    if json_file == None:
        return getResponse(400, "json_file is empty")
    filename: str = json_file.filename.strip()
    service_name, service_type = extract_service_info(filename)
    if service_name == "":
        return getResponse(400, "service_name is empty")
    if service_type != ".json":
        return getResponse(400, "service_type is not json")
    
    # 将json文件转为dict变量
    service_dict = json.load(json_file)
    # 生成hpp模板
    hpp_template: str = Util.parseJsonToH(service_dict)
    # 生成cpp模板
    cpp_template: str = Util.parseJsonToCPP(service_dict)
    # 保存文件
    return getResponse(200, "success", {"hpp_template": hpp_template, "cpp_template": cpp_template})


def async_process_service(service: AtomService):
    """
    异步处理服务
    """
    service_name = service._base_info.get_name()
    # 解析 cpp 文件
    AtomServiceUtil.parseCpp(
        service,
        os.path.join(STAGING_DIR, service_name, f"{service_name}.cpp"),
    )
    # 生成微服务编译的cmake
    AtomServiceUtil.generateAtomServiceCMakeLists(service)

    # idl生成编译相关函数
    DDSUtil.generateIdl(service)
    DDSUtil.generateIdlCMakeLists(service)
    DDSUtil.compileIdl(service)

    # proto生成编译相关函数
    GrpcMethodUtil.generateProtoFile(service)
    GrpcMethodUtil.generateProtoCmakeLists(service)
    GrpcMethodUtil.compileProtoFile(service)

    # ServerImpl 生成编译相关函数
    GrpcMethodUtil.generateServerImpl(service)
    GrpcMethodUtil.generateServerImplCMakeLists(service)
    GrpcMethodUtil.compileServerImpl(service)

    # ClientImpl 生成编译相关函数
    GrpcMethodUtil.generateStubImpl(service)
    GrpcMethodUtil.generateClientImplCMakeLists(service)
    GrpcMethodUtil.compileClientImpl(service)
